using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System.Net;
using System.Xml;

using Microsoft.Crm.Sdk.Proxy;
using Microsoft.Crm.Sdk.Discovery.Proxy;
using Microsoft.Crm.Passport.IdCrl;

namespace ImportCustomization
{
    class ImportCustomization
    {
        string m_XmlFileName;
        XmlDocument m_XmlDoc;

        private CrmService m_CrmService;
        private string m_WebServer;
        private string m_DiscoveryServer;
        private string m_Partner;
        private string m_Organization;
        private string m_DomainName;
        private string m_UserName;
        private string m_Password;
        private string m_Authentication = "AD";
        private string m_WebServiceUrl = "http://localhost/MSCRMServices/2007/CRMService.asmx";
        private bool m_ssl = false;

        private readonly Guid m_serviceId = new Guid("{DF60E2DF-88AD-4526-AE21-83D130EF0FA8}");
        private string PassportEnvironment = "INT";

        private bool m_foundAllArgs = false;

        static void Main(string[] args)
        {
            if (args.Length == 0 || args[0] == "/?")
            {
                Console.WriteLine("Arguments missing:");
                Console.WriteLine(" XmlConfigFile");
                Console.WriteLine(" /webserver:[Name of crm server]");
                Console.WriteLine(" /orgname:[Name of Organization]");
                Console.WriteLine(" /domainname:[Name of Domain only if AD Authentication]");
                Console.WriteLine(" /username:[Name of DomainName or Passport Login]");
                Console.WriteLine(" /password:[Password]");
                Console.WriteLine(" /authentication:[AD or passport]");
                Console.WriteLine(" /discoveryserver:[Name of discovery service server]");
                Console.WriteLine(" /partner:[Name of site registered at Passport]");
                Console.WriteLine(" /ssl:[true or false]");
                Console.WriteLine(" /passportenvironment:[Eg: INT]");
                Environment.Exit(1);
            }
            ImportCustomization importCustomization = new ImportCustomization(args);
            if (importCustomization.m_foundAllArgs)
            {
                importCustomization.ImportXml();
                importCustomization.PublishCustomizations();                
            }
        }

        public ImportCustomization(string[] args)
        {            
            bool foundWebServer = false;
            bool foundOrgName = false;
            bool foundDomainName = false;
            bool foundUserName = false;
            bool foundPassword = false;            

            m_XmlFileName = args[0];

            for (int i = 1; i < args.Length; i++)
            {
                if (args[i].Contains("/webserver:"))
                {
                    m_WebServer = args[i].Substring(args[i].IndexOf(':') + 1);
                    m_WebServiceUrl = m_WebServiceUrl.Replace("localhost", m_WebServer);
                    foundWebServer = true;
                }
                else if (args[i].Contains("/orgname:"))
                {
                    m_Organization = args[i].Substring(args[i].IndexOf(':') + 1);
                    foundOrgName = true;
                }
                else if (args[i].Contains("/domainname:"))
                {
                    m_DomainName = args[i].Substring(args[i].IndexOf(':') + 1);
                    foundDomainName = true;
                }
                else if (args[i].Contains("/username:"))
                {
                    m_UserName = args[i].Substring(args[i].IndexOf(':') + 1);
                    foundUserName = true;
                }
                else if (args[i].Contains("/password:"))
                {
                    m_Password = args[i].Substring(args[i].IndexOf(':') + 1);
                    foundPassword = true;
                }
                else if (args[i].Contains("/authentication:"))
                {
                    m_Authentication = args[i].Substring(args[i].IndexOf(':') + 1);
                    
                }
                else if (args[i].Contains("/discoveryserver:"))
                {
                    m_DiscoveryServer = args[i].Substring(args[i].IndexOf(':') + 1);                   
                }
                else if (args[i].Contains("/partner:"))
                {
                    m_Partner = args[i].Substring(args[i].IndexOf(':') + 1);                    
                }
                else if (args[i].Contains("/passportenvironment:"))
                {
                    PassportEnvironment = args[i].Substring(args[i].IndexOf(':') + 1);
                }
                else if (args[i].Contains("/ssl:"))
                {
                    m_ssl = bool.Parse(args[i].Substring(args[i].IndexOf(':') + 1));
                    if (m_ssl == true)
                    {
                        m_WebServiceUrl = m_WebServiceUrl.Replace("http://", "https://");
                    }
                }
            }
            if (m_Authentication.ToLower().Equals("ad"))
            {
                if (foundWebServer && foundOrgName && foundDomainName && foundUserName && foundPassword)
                {
                    m_foundAllArgs = true;
                }
                else
                {
                    Console.WriteLine("Arguments missing:");
                    Console.WriteLine("1. XmlConfigFile");
                    Console.WriteLine("2. /webserver:[Name of crm server]");
                    Console.WriteLine("3. /orgname: [Name of Organization]");
                    Console.WriteLine("4. /domainname: [Name of Domain]");
                    Console.WriteLine("5. /username: [Name of User]");
                    Console.WriteLine("6. /password: [Password]");
                    Console.WriteLine("7. /authentication: [AD or passport]");
                    Console.WriteLine("8. /discoveryserver:[Name of discoveryservice server, defaults to webserver if missing]");
                    Environment.Exit(1);
                }
            }
            else if(m_Authentication.ToLower().Equals("passport"))
            {
                if (foundWebServer && foundOrgName && foundUserName && foundPassword)
                {
                    m_foundAllArgs = true;
                }
                else
                {
                    Console.WriteLine("Arguments missing:");
                    Console.WriteLine("1. XmlConfigFile");
                    Console.WriteLine("2. /webserver:[Name of crm server]");
                    Console.WriteLine("3. /orgname: [Name of Organization]");                    
                    Console.WriteLine("4. /username: [Name of User]");
                    Console.WriteLine("5. /password: [Password]");
                    Console.WriteLine("6. /authentication: [AD or passport]");
                    Console.WriteLine("7. /discoveryserver:[Name of discoveryservice server defaults to webserver]");
                    Console.WriteLine("8. /partner:[Name of partner site registered at passport defaults to webserver]");
                    Environment.Exit(1);
                }
            }

            if(m_DiscoveryServer == null)
            {
                m_DiscoveryServer = m_WebServer;
            }
            if (m_Partner == null)
            {
                m_Partner = m_WebServer;
            }

            if (m_Authentication.ToLower().Equals("ad"))
            {
                NetworkCredential currentUserCredentials = new NetworkCredential(m_UserName, m_Password, m_DomainName);

                m_CrmService = new CrmService();
                m_CrmService.Credentials = currentUserCredentials;
                m_CrmService.UnsafeAuthenticatedConnectionSharing = true;
                m_CrmService.ConnectionGroupName = m_DomainName + "\\" + m_UserName;
                m_CrmService.Url = m_WebServiceUrl;
                CrmAuthenticationToken token = new CrmAuthenticationToken();
                token.OrganizationName = m_Organization;
                token.AuthenticationType = AuthenticationType.AD;
                m_CrmService.CrmAuthenticationTokenValue = token;
                m_CrmService.Timeout = 1000000;
            }
            else if (m_Authentication.ToLower().Equals("passport"))
            {

                string crmTicket = "";
                
                try
                {
                    // Need to get the CrmTicket
                    CrmDiscoveryService discService = CreateDiscoveryService();
                    RetrieveCrmTicketRequest ticketRequest = new RetrieveCrmTicketRequest();
                    ticketRequest.OrganizationName = m_Organization;
                    ticketRequest.PassportTicket = GetPassportTicket(RetrievePolicy(), m_UserName, m_Password, m_Partner);
                    RetrieveCrmTicketResponse ticketResponse = (RetrieveCrmTicketResponse)discService.Execute(ticketRequest);

                    crmTicket = ticketResponse.CrmTicket;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                    Environment.Exit(1);
                }
                

                // Retrieve the information
                m_CrmService = new CrmService();                
                m_CrmService.Url = m_WebServiceUrl;
                CrmAuthenticationToken token = new CrmAuthenticationToken();
                token.OrganizationName = m_Organization;
                token.AuthenticationType = AuthenticationType.Passport;
                token.CrmTicket = crmTicket;

                m_CrmService.CrmAuthenticationTokenValue = token;
                m_CrmService.Timeout = 1000000;
            }
        }

        public CrmDiscoveryService CreateDiscoveryService()
        {
            CrmDiscoveryService service = new CrmDiscoveryService();
            string targetHostName;
            if (m_WebServer == "")
            {
                targetHostName = "http://localhost";
            }
            else
            {
                targetHostName = "http://" + m_DiscoveryServer;
            }

            service.Url = string.Format("{0}/MSCRMServices/2007/{1}/CrmDiscoveryService.asmx", targetHostName, "Passport");

            if (m_ssl == true)
            {
                service.Url = service.Url.Replace("http://", "https://");
            }

            return service;
        }

        public string GetPassportTicket(string policy, string userName, string password, string passportSiteDomain)
        {
            using (LogonManager lm = new LogonManager())
            {
                try
                {
                    Console.WriteLine("Policy : " + policy);
                    Console.WriteLine("UserName: " + userName);
                    Console.WriteLine("password: " + password);
                    Console.WriteLine("passportSiteDomain: " + passportSiteDomain);
                    Console.WriteLine("Service Id: " + m_serviceId);
                    Console.WriteLine("Calling Passport Logon now...");
                    string ticket = lm.Logon(m_serviceId,
                        userName,
                        password,
                        passportSiteDomain,
                        policy, PassportEnvironment);

                    Console.WriteLine("Passport Ticket returned: " + ticket);

                    return ticket;
                }
                catch (Exception e)
                {
                    Console.WriteLine("IDCRL Exception: " + e.ToString());
                    throw (e);
                }
            }
        }

        public string RetrievePolicy()
        {
            try
            {
                CrmDiscoveryService service = CreateDiscoveryService();
                RetrievePolicyRequest policyRequest = new RetrievePolicyRequest();
                RetrievePolicyResponse policyResponse = (RetrievePolicyResponse)service.Execute(policyRequest);

                if (policyResponse.Policy != "")
                {
                    return policyResponse.Policy;
                }
                else
                {
                    Console.WriteLine("Error when getting Retrieve Policy: PolicyResponse.Policy is empty, sending default - NFS_2HR_0_COMPACT");
                    return "NFS_2HR_0_COMPACT";
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception when calling Discovery Service: " + e.ToString());
                Console.WriteLine("Defaulting to return NFS_2HR_0_COMPACT");
                return "NFS_2HR_0_COMPACT";
            }
        }

        public void ImportXml()
        {
            try
            {
                m_XmlDoc = new XmlDocument();
                m_XmlDoc.Load(m_XmlFileName);

                ImportAllXmlRequest req = new ImportAllXmlRequest();
                req.CustomizationXml = m_XmlDoc.OuterXml;

                ImportAllXmlResponse resp = (ImportAllXmlResponse)m_CrmService.Execute(req);
                Console.WriteLine("The Custom Entity has been imported");
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                Console.WriteLine("Soap Exception: " + ex.Detail);
            }
            catch (Exception genEx)
            {
                Console.WriteLine(genEx.ToString());
            }
        }

        public void PublishCustomizations()
        {
            PublishAllXmlRequest pub = new PublishAllXmlRequest();
            try
            {
                PublishAllXmlResponse resp = (PublishAllXmlResponse)m_CrmService.Execute(pub);
                Console.WriteLine("Published all custom entities");
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }        
    }
}
